"""
Copyright (c) 2022 Huawei Technologies Co.,Ltd.

openGauss is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:

          http://license.coscl.org.cn/MulanPSL2

THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
"""
"""
Case Type   : 极致RTO-数据库系统类
Case Name   : 开启极致RTO，备机进行build时，备机异常挂住后恢复，关闭极致RTO，主备数据一致
Create At   : 2020/10/22
Owner       : @peilinqian
Description :
    1、开启极致RTO
    2、备机数据库进程挂住kill - 19；查询备机数据数据进程状态
    3、备机进行重建
    3.5、恢复备机数据库进程kill - 18
    4、执行完成后，关闭极致RTO
    5、查询主备数据是否一致
Expect      :
    1、开启极致RTO成功
    2、备机数据库进程挂住kill - 19；查询备机数据数据进程状态Tl
    3、备机进行重建成功
    3.5、恢复备机数据库进程kill - 18
    4、执行完成后，关闭极致RTO成功
    5、查询主备数据一致
History     :
    modified：2021-3-9 by 5328113;主备同步方法调用修改，增加超时等待时间；
    modified：2021-2-5 by 5328113;guc参数还原时指定还原值
    modified:2020-11-18 by @peilinqian ；修改kill-18后的断言；可能存在Rl状态；
    modified:2020-12-14 by @peilinqian ；删除kill-19、kill -18后的断言；
        变化的状态可能很多状态变化时间也不一定，只需保证后续恢复后功能正常即可；
    modified:2020-12-15 by @peilinqian ;teardown增加异常中断kill -18恢复操作；
    modified:2021-1-28 by 5328113;删除start成功断言，删除不必要tpcc
"""

import time
import unittest

from testcase.utils.ComThread import ComThread
from testcase.utils.Common import Common
from testcase.utils.CommonSH import *
from testcase.utils.Constant import Constant
from testcase.utils.Logger import Logger
from yat.test import Node
from yat.test import macro


class ExtremeRto(unittest.TestCase):
    nodes_tuple = ('PrimaryDbUser', 'Standby1DbUser', 'Standby2DbUser')

    @RestartDbCluster(*nodes_tuple)
    def setUp(self):
        self.log = Logger()
        self.log.info('----Opengauss_Reliability_RTO_Dbsys_Case018:初始化----')

        self.pri_root = Node(node='PrimaryRoot')
        self.pri_dbuser = Node(node='PrimaryDbUser')
        self.sta1_root = Node(node='Standby1Root')
        self.sta1_dbuser = Node(node='Standby1DbUser')
        self.sta2_dbuser = Node(node='Standby2DbUser')
        self.pri_sh = CommonSH('PrimaryDbUser')
        self.sta_1_sh = CommonSH('Standby1DbUser')
        self.sta_2_sh = CommonSH('Standby2DbUser')
        self.com = Common()
        self.constant = Constant()
        self.tpcc_path = macro.TPCC_PATH
        self.node_num = self.com.get_node_num(self.pri_root)
        self.log.info(self.node_num)
        self.max_time = 1200
        self.min_time = 300

    def test_main(self):
        try:
            self.log.info('----1、开启极致RTO----')
            msg = self.pri_sh.executeGsguc('set',
                                           self.constant.GSGUC_SUCCESS_MSG,
                                           'replication_type=1')
            self.assertTrue(msg)
            msg = self.pri_sh.executeGsguc('set',
                                           self.constant.GSGUC_SUCCESS_MSG,
                                           'recovery_parse_workers=4')
            self.assertTrue(msg)
            msg = self.pri_sh.executeGsguc('set',
                                           self.constant.GSGUC_SUCCESS_MSG,
                                           'recovery_redo_workers=4')
            self.assertTrue(msg)
            msg = self.pri_sh.executeGsguc('set',
                                           self.constant.GSGUC_SUCCESS_MSG,
                                           'hot_standby=off')
            self.assertTrue(msg)
            # 重启数据库配置文件生效
            self.pri_sh.stopDbCluster()
            time.sleep(2)
            self.pri_sh.startDbCluster()

            self.log.info('----查看主机query，同步是否正常----')
            self.pri_sh.check_location_consistency('primary',
                                                   self.node_num,
                                                   self.min_time)

            self.log.info('----查询主备状态----')
            status = self.pri_sh.getDbClusterStatus('status')
            self.assertTrue(status)

            self.log.info('----2、备机数据库进程挂住kill - 19；----')
            self.com.kill_pid_keyword('M standby', 19, self.sta1_dbuser)
            time.sleep(1)

            self.log.info('查询备机数据数据进程状态')
            db_process_cmd = 'ps ux | grep \'M standby\' | grep -v grep'
            self.log.info(db_process_cmd)
            db_process_result = self.sta1_dbuser.sh(db_process_cmd).result()
            self.log.info(db_process_result)

            self.log.info('----3、备机进行重建----')
            build_thread = ComThread(self.sta_1_sh.executeGsctl, args=(
                'build', self.constant.REBUILD_SUCCESS_MSG,))
            build_thread.setDaemon(True)
            build_thread.start()
            time.sleep(5)

            self.log.info('查询备机数据数据进程状态')
            db_process_cmd = 'ps ux | grep \'M standby\' | grep -v grep'
            self.log.info(db_process_cmd)
            db_process_result = self.sta1_dbuser.sh(db_process_cmd).result()
            self.log.info(db_process_result)

            if db_process_result != '':
                self.log.info('----4、恢复备机数据库进程kill - 18----')
                self.com.kill_pid_keyword('M standby', 18, self.sta1_dbuser)

                time.sleep(2)
                self.log.info('查询备机数据数据进程状态')
                db_process_cmd = 'ps ux | grep \'M standby\' | grep -v grep'
                self.log.info(db_process_cmd)
                db_process_result = self.sta1_dbuser.sh(
                    db_process_cmd).result()
                self.log.info(db_process_result)

            # 等待build重建完成
            build_thread.join()
            build_result = build_thread.get_result()
            # 如果重建失败，再次备机重建，进行备机恢复
            if not build_result:
                # 再次重建备机成功
                rebuild_result = self.sta_1_sh.build_fail_scp_crtfile()
                if rebuild_result:
                    self.log.info('用例执行异常，重建失败后，'
                                  '再次进行备机重建成功，已恢复')
                else:
                    self.log.info('用例执行异常，重建失败后，'
                                  '再次进行备机重建失败，请检查失败原因')
                self.assertTrue(rebuild_result)

        finally:
            self.log.info('----4、执行完成后，关闭极致RTO----')
            msg = self.pri_sh.executeGsguc('set',
                                           self.constant.GSGUC_SUCCESS_MSG,
                                           'recovery_parse_workers = 1')
            self.assertTrue(msg)
            msg = self.pri_sh.executeGsguc('set',
                                           self.constant.GSGUC_SUCCESS_MSG,
                                           'recovery_redo_workers = 1')
            self.assertTrue(msg)
            msg = self.pri_sh.executeGsguc('set',
                                           self.constant.GSGUC_SUCCESS_MSG,
                                           'hot_standby=on')
            self.assertTrue(msg)
            # 重启数据库配置文件生效
            self.pri_sh.stopDbCluster()
            time.sleep(2)
            self.pri_sh.startDbCluster()

            # 校验备机是否完成数据同步
            self.log.info('校验备机是否完成数据同步')
            flag_1 = self.sta_1_sh.check_location_consistency('standby',
                                                              self.node_num,
                                                              self.min_time)
            self.assertTrue(flag_1)
            flag_2 = self.sta_2_sh.check_location_consistency('standby',
                                                              self.node_num,
                                                              self.min_time)
            self.assertTrue(flag_2)

            self.log.info('----5、查询主备数据是否一致----')
            nodes_tuple = (self.pri_dbuser,
                           self.sta1_dbuser,
                           self.sta2_dbuser)
            flag = self.com.check_data_sample_by_all(r'\d', *nodes_tuple)
            self.assertTrue(flag)

            table_dict = self.com.format_sql_result(
                self.pri_sh.executDbSql(r'\d'))
            table_name = table_dict.get('Name')
            for name in table_name:
                select_sql = f'select count(*) from {name};'
                flag = self.com.check_data_sample_by_all(select_sql,
                                                         *nodes_tuple)
                self.assertTrue(flag)

    def tearDown(self):

        self.log.info('----异常中断时，恢复数据库进程kill - 18----')
        self.com.kill_pid_keyword('M standby', 18, self.sta1_dbuser)
        time.sleep(1)
        self.log.info('查询备机数据数据进程状态')
        db_process_cmd = 'ps ux | grep \'M standby\' | grep -v grep'
        self.log.info(db_process_cmd)
        db_process_result = self.sta1_dbuser.sh(db_process_cmd).result()
        self.log.info(db_process_result)

        self.log.info('----校验刷脏页是否完成----')
        self.pri_sh.check_whether_remain_dirty_page()

        self.log.info('----Opengauss_Reliability_RTO_Dbsys_Case018:执行完毕----')
